/**********************************************************

@description:
*File Name:loader_32.c
*Author: ydx
*mail: yudongxiang93@163.com
*Create Time: Sat 31 May 2025 03:05:55 PM CST
**********************************************************/
#include "loader.h"
#include "elf.h"

extern boot_info_t boot_info;
// 直接端口访问（PIO 模式）
// 在 保护模式 或 没有 BIOS 支持 时，可以通过 硬盘控制器端口（Primary IDE: 1F0h~1F7h）直接读写。
static void read_disk(uint32_t sector, uint32_t count, uint8_t *buf)
{
    outb(0x1F6, 0xE0);
    outb(0x1F2, (uint8_t)(count >> 8));
    outb(0x1F3, (uint8_t)(sector >> 24));    // start sector
    outb(0x1F4, 0);
    outb(0x1F5, 0);

    outb(0x1F2, (uint8_t)count);
    outb(0x1F3, (uint8_t)(sector));
    outb(0x1F4, (uint8_t)(sector >> 8));
    outb(0x1F5, (uint8_t)(sector >> 16));

    outb(0x1F7, 0x24);
    uint16_t *data_buf = (uint16_t *)buf;
    while (count--)
    {
        while ((inb(0x1F7) & 0x88) != 0x8)
        {
        }
        for (int i = 0; i < SECTOR_SIZE / 2; i++)
        {
            *data_buf++ = inw(0x1F0);
        }
    }
}
static uint32_t reload_elf_file(uint8_t *file_buffer)
{
    uint8_t     a1   = (uint8_t)((uint32_t)ELF_MAGIC >> 24);
    uint8_t     a2   = (uint8_t)((uint32_t)ELF_MAGIC >> 16);
    uint8_t     a3   = (uint8_t)((uint32_t)ELF_MAGIC >> 8);
    uint8_t     a4   = (uint8_t)((uint32_t)ELF_MAGIC & 0xFF);
    Elf32_Ehdr *ehdr = (Elf32_Ehdr *)file_buffer;
    if ((ehdr->e_ident[0] != (uint8_t)(ELF_MAGIC >> 24)) || (ehdr->e_ident[1] != (uint8_t)(ELF_MAGIC >> 16)) ||
        (ehdr->e_ident[2] != (uint8_t)(ELF_MAGIC >> 8)) || (ehdr->e_ident[3] != (uint8_t)(ELF_MAGIC & 0xFF)))
    {
        return 0;
    }
    for (int i = 0; i < ehdr->e_phnum; i++)
    {
        Elf32_Phdr *phdr = (Elf32_Phdr *)(file_buffer + ehdr->e_phoff) + i;
        if (phdr->p_type != PT_LOAD)
        {
            continue;
        }
        uint8_t *src  = file_buffer + phdr->p_offset;
        uint8_t *dest = (uint8_t *)phdr->p_paddr;
        for (int j = 0; j < phdr->p_filesz; j++)
        {
            *dest++ = *src++;
        }
        // clear bss
        dest = (uint8_t *)phdr->p_paddr + phdr->p_filesz;
        for (int j = 0; j < phdr->p_memsz - phdr->p_filesz; j++)
        {
            *dest++ = 0;
        }
    }
    return ehdr->e_entry;
}
static void die(int code)
{
    for (;;)
    {
    }
}

#define PDE_P   (1 << 0)     // pde present
#define PDE_W   (1 << 1)     // write enabled
#define PDE_PS  (1 << 7)     // page_size =1MB
#define CR4_PSE (1 << 4)     // the size of page is 4MB
#define CR0_PG  (1 << 31)    // open page

void enable_page_mode(void)
{
    static uint32_t page_dir[1024] __attribute__((aligned(4096))) = {
        [0] = PDE_P | PDE_W | PDE_PS | 0,
    };
    uint32_t cr4 = read_cr4();
    write_cr4(cr4 | CR4_PSE);
    write_cr3((uint32_t)page_dir);
    write_cr0(read_cr0() | CR0_PG);
}

void load_kernel(void)
{
    read_disk(100, 500, (uint8_t *)SYS_KERNEL_LOAD_ADDR);
    uint32_t kernel_entry = reload_elf_file((uint8_t *)SYS_KERNEL_LOAD_ADDR);
    if (kernel_entry == 0)
    {
        die(-1);
    }
    enable_page_mode();
    //((void(*)(void))SYS_KERNEL_LOAD_ADDR)();
    ((void (*)(boot_info_t *))kernel_entry)(&boot_info);
    for (;;)
    {
    }
}